/*jslint browser: true, devel: true, bitwise: true, white: true, plusplus: true, forin:true, nomen:true */
/*global $:true, define:true */

/**
 * @module Test
 */

define(['WID/Test/Matchers','WID/Test/NeedHelper'],function(Matchers, NeedHelper) {
"use strict";

/** 
 * 
 * @class TestCase
 * @constructor
 */
function TestCase(reporter) {
	this.Data = undefined;
	this.each = undefined;
	this.before = undefined;
	this.after = undefined;
	
	this.TestMatcher = new Matchers();
	this.CurrentExpect = -1; 
	this.CurrentResults = {};
	this.Reporter = reporter;
}

TestCase.EmptyCallback = function() {};

TestCase.prototype = {
	constructor: TestCase,
	
	setData: function(data) {
		this.Data = data;
		this.each = data.each;
		this.before = data.beforeEach||TestCase.EmptyCallback;
		this.after = data.afterEach||TestCase.EmptyCallback;
	},
	
	beforeEach: function() {
		this.CurrentExpect = -1;
		this.CurrentResults = {};
		this.before();
	},
	
	afterEach: function() {
		this.after();
	},
	
	describe: function(desc) {
		this.CurrentResults.desc = desc;
	},
	 
	expect: function(exp) {
		var lastLog = this.TestMatcher.log;
		if (lastLog) {
			this.logFailure(lastLog);
		}
		this.TestMatcher.newExpect(++this.CurrentExpect,exp);
		
		return this.TestMatcher;
	},
	
	exec: function(data) {
		this.setData(data);
		this.Reporter.caseStarted({
			name:this.Data.name,
			desc:this.Data.desc, 
			total:this.Data.tests.length
		});
		
		this.testing(-1);
		this.Reporter.caseDone();
	},
	
	testing: function(last) {
		var i,tests = this.Data.tests,len = tests.length;
		try {
			for (i = last+1; i < len; ++i) {
					this.test(tests[i]);
			}
		} catch (e) {
			this.logError('Unexpected error: '+e.name+' near excepted '+ this.CurrentExpect,e.stack.split('\n'));
			this.Reporter.testDone(this.CurrentResults);
			this.testing(i);	
		}	
	},
	
	test: function(testFunction) {
		this.Reporter.testStarted({name:testFunction.name});
		
		this.beforeEach();
			testFunction.call(this,this.each);
		this.afterEach();
		
		var lastLog = this.TestMatcher.log;
		if (lastLog) {
			this.logFailure(lastLog);
		}
		
		if (this.CurrentExpect === -1) {
			this.Reporter.reportEmptyTest();
		} else {
			this.Reporter.testDone(this.CurrentResults);
		}
		
	},
	
	logFailure: function(log) {
		var results = this.CurrentResults;
		results.failed = true;
		if (!results.exps) {
			results.exps = [];
		}
		results.exps[results.exps.length] = log;
	},
	
	logError: function(name,stack) {
		var results = this.CurrentResults;
		results.failed = true;
		results.error = {name:name,stack:stack};
	}
};

Object.defineProperty(TestCase.prototype,'need',{
	get: function() {
		return NeedHelper;
	}
});

return TestCase;
});
